#include "Packet.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include "stdGNUTDefs.h"

/**
 * Setup a completely empty packet
 */
Packet::Packet()
{
	myPacketByteArray = NULL;
	memset((char*)(&(myPacket.header)), 0, sizeof(GNUT_Header));
	myPacket.payloadLen = 0;
	myPacket.payload = NULL;
}

/**
 * This constructor builds a packet out of the specified
 * parameters.
 */
Packet::Packet(short srcPort, short dstPort, int SeqAckNo, int AdvWindow, int type, char* payload, int length)
{
	myPacketByteArray = NULL;

	myPacket.header.srcPort = htons(srcPort);
	myPacket.header.dstPort = htons(dstPort);
	myPacket.header.SeqAckNo = htonl(SeqAckNo);
	myPacket.header.AdvWindow = htonl(AdvWindow);
	myPacket.header.TypeSize = htons((unsigned short)((type << 13) + (sizeof(GNUT_Header) + length)));
	myPacket.payload = payload;
	myPacket.payloadLen = length;
	myPacket.header.CheckSum = computeCheckSum();
}

/** 
 * This constructor builds the packet out of a *received*
 * string of bytes... this is important because the packet
 * constructor assumes that the string of bytes has header
 * fields in network byte order.
 */
Packet::Packet(char* buffPtr, unsigned int length)
{
	myPacketByteArray = NULL;

	// TODO: unsigned comparison... ok?
	if( length >= sizeof(GNUT_Header_t) ) 
	{
		GNUT_Header_t * headerPtr;

		headerPtr = reinterpret_cast<GNUT_Header_t*>(buffPtr);

		myPacket.header = *headerPtr;

		myPacket.payloadLen = length - sizeof(GNUT_Header_t);
		if( myPacket.payloadLen != 0 )
			myPacket.payload = buffPtr + sizeof(GNUT_Header_t);
		else
			myPacket.payload = NULL;
	} else
	{
		memset((char*)(&(myPacket.header)), 0, sizeof(GNUT_Header));
		myPacket.payloadLen = 0;
		myPacket.payload = NULL;
	}
}

/** 
 * This function "reconstructs" a currently instantiated
 * packet to be the packet described by the parameters given
 */
void Packet::setPacket(short srcPort, short dstPort, int SeqAckNo, int AdvWindow, int type, char* payload, int length)
{
	myPacketByteArray = NULL;

	myPacket.header.srcPort = htons(srcPort);
	myPacket.header.dstPort = htons(dstPort);
	myPacket.header.SeqAckNo = htonl(SeqAckNo);
	myPacket.header.AdvWindow = htonl(AdvWindow);
	myPacket.header.TypeSize = htons((unsigned short)((type << 13) + (sizeof(GNUT_Header) + length)));
	myPacket.payload = payload;
	myPacket.payloadLen = length;
	myPacket.header.CheckSum = computeCheckSum();
}

/** 
 * This function "reconstructs" a currently instantiated
 * packet to be the packet described in buffPtr
 */
void Packet::setPacket(char* buffPtr, unsigned int length)
{
	myPacketByteArray = NULL;

	// TODO: unsigned comparison... ok?
	if( length >= sizeof(GNUT_Header_t) ) 
	{
		GNUT_Header_t * headerPtr;

		headerPtr = reinterpret_cast<GNUT_Header_t*>(buffPtr);

		myPacket.header = *headerPtr;

		myPacket.payloadLen = length - sizeof(GNUT_Header_t);
		if( myPacket.payloadLen != 0 )
			myPacket.payload = buffPtr + sizeof(GNUT_Header_t);
		else
			myPacket.payload = NULL;
	} else
	{
		memset((char*)(&(myPacket.header)), 0, sizeof(GNUT_Header));
		myPacket.payloadLen = 0;
		myPacket.payload = NULL;
	}
}


// Accessor functions

unsigned short Packet::getSrcPort()
{
	return ntohs(myPacket.header.srcPort);
}

unsigned short Packet::getDstPort()
{
	return ntohs(myPacket.header.dstPort);
}

unsigned int Packet::getSeqNo()
{
	return ntohl(myPacket.header.SeqAckNo);
}

unsigned int Packet::getAckNo()
{
	return ntohl(myPacket.header.SeqAckNo);
}

unsigned int Packet::getAdvWindow()
{
	return ntohl(myPacket.header.AdvWindow);
}

// return type on type? I just picked short
unsigned short Packet::getType()
{
	return (ntohs(myPacket.header.TypeSize) >> 13);
}

unsigned short Packet::getSize()
{
	return (ntohs(myPacket.header.TypeSize) & 0x1FFF);
}

unsigned short Packet::getCheckSum()
{
	return myPacket.header.CheckSum;
}

char* Packet::getPayload()
{
	return myPacket.payload;
}

unsigned int Packet::getPayloadLength()
{
	return myPacket.payloadLen;
}

void Packet::setSrcPort(unsigned short srcPort)
{
	myPacket.header.srcPort = htons(srcPort);
	updateCheckSum();
}

void Packet::setDstPort(unsigned short dstPort)
{
	myPacket.header.dstPort = htons(dstPort);
	updateCheckSum();
}

void Packet::setSeqNo(unsigned int seqNo)
{
	myPacket.header.SeqAckNo = htonl(seqNo);
	updateCheckSum();
}

void Packet::setAckNo(unsigned int ackNo)
{
	myPacket.header.SeqAckNo = htonl(ackNo);
	updateCheckSum();
}

void Packet::setAdvWindow(unsigned int advWindow)
{
	myPacket.header.AdvWindow = htonl(advWindow);
	updateCheckSum();
}

void Packet::setType(unsigned int type)
{
	myPacket.header.TypeSize = ntohs(myPacket.header.TypeSize);
	myPacket.header.TypeSize = myPacket.header.TypeSize & 0x1FFF;
	myPacket.header.TypeSize = myPacket.header.TypeSize | ((unsigned short)(type << 13));
	myPacket.header.TypeSize = htons(myPacket.header.TypeSize);
	updateCheckSum();
}

void Packet::updateSize()
{
	myPacket.header.TypeSize = ntohs(myPacket.header.TypeSize);
	myPacket.header.TypeSize = myPacket.header.TypeSize & 0xE000;
	myPacket.header.TypeSize = myPacket.header.TypeSize | (((unsigned short)(sizeof(GNUT_Header) + myPacket.payloadLen)) & 0x1FFF);
	myPacket.header.TypeSize = htons(myPacket.header.TypeSize);
}

void Packet::setPayload(char* payload, int length)
{
	myPacket.payload = payload;
	myPacket.payloadLen = length;
	updateSize();
	updateCheckSum();
}

void Packet::updateCheckSum()
{
	myPacket.header.CheckSum = computeCheckSum();
}

// Packet type functions

bool Packet::isACK()
{
	return (getType() == ACK);
}

bool Packet::isSYN()
{
	return (getType() == SYN);
}

bool Packet::isFIN()
{
	return (getType() == FIN);
}

bool Packet::isRST()
{
	return (getType() == RST);
}

bool Packet::isDAT()
{
	return (getType() == DAT);
}

/**
 * When we need to send the packet, we will need to turn it
 * into a buffer pointer to be fed to MNL_sendTo()...
 * This function does exactly that...
 */
char* Packet::toBytes()
{
	if( myPacketByteArray == NULL )
	{
		myPacketByteArray = (char*)malloc(getSize()*sizeof(char));

		// maybe use reinterpret_cast???
		memcpy(myPacketByteArray, reinterpret_cast<char*>(&(myPacket.header)), sizeof(GNUT_Header_t));

		memcpy(myPacketByteArray + sizeof(GNUT_Header_t), myPacket.payload, myPacket.payloadLen);
	}

	return myPacketByteArray;
}

/**
 * Free the memory allocated during a call to toBytes()
 */
void Packet::freeBytes()
{
	if( myPacketByteArray != NULL )
	{
		free(myPacketByteArray);
		myPacketByteArray = NULL;
	}
}

/**
 * Checks the checksum on the packet.
 * 
 * returns true if good, false if bad
 */
bool Packet::checkCheckSum()
{
	//printf("Checking check sum -- Stored: %d, ntohs(Stored): %d, computed: %d, htons(computed): %d\n", myPacket.header.CheckSum, ntohs(myPacket.header.CheckSum),computeCheckSum(), htons(computeCheckSum()));
	return true;
	//return (myPacket.header.CheckSum == computeCheckSum());
}

/**
 * Computes the checksum for myPacket by calculating the one's complement sum
 * of all 16 bit chunks in the the payload as well as the header, and then 
 * taking the one's complement.
 * 
 * returns computed checksum value
 */
unsigned short Packet::computeCheckSum()
{
	unsigned short checkSum = 0;
	unsigned short halfWord;
	unsigned int index;

	// Checksum for payload
	for( index = 0; index < myPacket.payloadLen; index += sizeof(short) ) {
		if( myPacket.payloadLen - index >= sizeof(short) )
			halfWord = (unsigned short)((myPacket.payload[index] << 8) + myPacket.payload[index+1]);
		else
			halfWord = (unsigned short)(myPacket.payload[index] << 8);

		checkSum += halfWord;
	}

	// Add checksum for header
	checkSum += (unsigned short)(myPacket.header.srcPort);
	checkSum += (unsigned short)(myPacket.header.dstPort);
	checkSum += (unsigned short)(myPacket.header.SeqAckNo & 0x0000FFFF);
	checkSum += (unsigned short)(myPacket.header.SeqAckNo >> 16);
	checkSum += (unsigned short)(myPacket.header.AdvWindow & 0x0000FFFF);
	checkSum += (unsigned short)(myPacket.header.AdvWindow >> 16);
	checkSum += (unsigned short)(myPacket.header.TypeSize);

	return checkSum;
}

// Pretty print purty packets
int Packet::printPacket()
{
	printf("-------------Packet-------------\n");
	printf("srcPort: \t%d\ndstPort: \t%d\n", getSrcPort(), getDstPort());
	printf("SeqAckNo: \t%d\n", getSeqNo());
	printf("AdvWindow: \t%d\n", getAdvWindow());
	printf("Type: \t\t%d\nSize: \t\t%d\nCheckSum: \t%d\n", getType(), getSize(), getCheckSum() );
	printf("DatLen: \t%d\n", getPayloadLength());
	printf("--------------------------------\n");
	
	return 1;
}

Packet::~Packet()
{
	if( myPacketByteArray != NULL )
	{
		free(myPacketByteArray);
		myPacketByteArray = NULL;
	}
}


